--
-- make_vala.lua
-- Generate a Vala project makefile.
--

	premake.make.vala = { }
	premake.make.makefile_ignore = false

	local vala = premake.make.vala
	local make = premake.make

	function premake.make_vala(prj)

		-- create a shortcut to the compiler interface
		local valac = premake.gettool(prj)

		-- build a list of supported target platforms that also includes a generic build
		local platforms = premake.filterplatforms(prj.solution, valac.platforms, "Native")

		-- output build configurations
		premake.gmake_vala_header(prj, valac, platforms)

		-- write configuration blocks
		for _, platform in ipairs(platforms) do
			for cfg in premake.eachconfig(prj, platform) do
				premake.gmake_valac_config(prj, cfg, valac)
			end
		end

		-- list object directories
		local objdirs = {}
		local additionalobjdirs = {}
		for _, file in ipairs(prj.allfiles) do
			if path.issourcefile(file) or path.isgresource(file) then
				objdirs[_MAKE.esc(path.getdirectory(path.trimdots(file)))] = 1
			end
		end

		_p('OBJDIRS := \\')
		_p('\t$(OBJDIR) \\')
		for dir, _ in iter.sortByKeys(objdirs) do
			_p('\t$(OBJDIR)/%s \\', dir)
		end
		for dir, _ in iter.sortByKeys(additionalobjdirs) do
			_p('\t%s \\', dir)
		end
		_p('')

		-- main build rule(s)
		_p('.PHONY: clean prebuild prelink')
		_p('')

		_p('all: $(OBJDIRS) $(TARGETDIR) prebuild prelink $(TARGET)')
		_p('\t@:')
		_p('')

		-- target build rule
		_p('$(TARGET): $(OBJECTS) | $(TARGETDIR)')
		_p('\t@echo Linking %s', prj.name)
		_p('\t$(SILENT) $(LINKCMD)')

		_p('\t$(POSTBUILDCMDS)')
		_p('')

		-- Create destination directories. Can't use $@ for this because it loses the
		-- escaping, causing issues with spaces and parenthesis
		_p('$(TARGETDIR):')
		premake.make_mkdirrule("$(TARGETDIR)")

		_p('$(OBJDIRS):')
		if (not prj.solution.messageskip) or (not table.contains(prj.solution.messageskip, "SkipCreatingMessage")) then
		_p('\t@echo Creating $(@)')
		end
		_p('\t-$(call MKDIR,$@)')
		_p('')

		-- clean target
		_p('clean:')
		if (not prj.solution.messageskip) or (not table.contains(prj.solution.messageskip, "SkipCleaningMessage")) then
			_p('\t@echo Cleaning %s', prj.name)
		end
		_p('ifeq (posix,$(SHELLTYPE))')
		_p('\t$(SILENT) rm -f  $(TARGET)')
		_p('else')
		_p('\t$(SILENT) if exist $(subst /,\\\\,$(TARGET)) del $(subst /,\\\\,$(TARGET))')
		_p('endif')
		_p('')

		-- custom build step targets
		_p('prebuild:')
		_p('\t$(PREBUILDCMDS)')
		_p('')

		_p('prelink:')
		_p('\t$(PRELINKCMDS)')
		_p('')

		vala.fileRules(prj, valac)
	end



--
-- Write the makefile header
--

	function premake.gmake_vala_header(prj, valac, platforms)
		_p('# %s project makefile autogenerated by GENie', premake.action.current().shortname)
		_p('')

		_p('.SUFFIXES:') -- Delete the default suffix rules.
		_p('')

		-- set up the environment
		_p('ifndef config')
		_p('  config=%s', _MAKE.esc(premake.getconfigname(prj.solution.configurations[1], platforms[1], true)))
		_p('endif')
		_p('')

		_p('ifndef verbose')
		_p('  SILENT = @')
		_p('endif')
		_p('')

		-- identify the shell type
		_p('SHELLTYPE := msdos')
		_p('ifeq (,$(ComSpec)$(COMSPEC))')
		_p('  SHELLTYPE := posix')
		_p('endif')
		_p('ifeq (/bin,$(findstring /bin,$(SHELL)))')
		_p('  SHELLTYPE := posix')
		_p('endif')
		_p('ifeq (/bin,$(findstring /bin,$(MAKESHELL)))')
		_p('  SHELLTYPE := posix')
		_p('endif')
		_p('')

		_p('ifeq (posix,$(SHELLTYPE))')
		_p('  MKDIR = $(SILENT) mkdir -p "$(1)"')
		_p('  COPY  = $(SILENT) cp -fR "$(1)" "$(2)"')
		_p('  RM    = $(SILENT) rm -f "$(1)"')
		_p('else')
		_p('  MKDIR = $(SILENT) mkdir "$(subst /,\\\\,$(1))" 2> nul || exit 0')
		_p('  COPY  = $(SILENT) copy /Y "$(subst /,\\\\,$(1))" "$(subst /,\\\\,$(2))"')
		_p('  RM    = $(SILENT) del /F "$(subst /,\\\\,$(1))" 2> nul || exit 0')
		_p('endif')
		_p('')

		_p('VALAC  = %s', valac.valac)
		_p('CC     = %s', valac.cc)
		_p('GLIBRC = %s', valac.glibrc)
		_p('')

		if (not premake.make.makefile_ignore) then
			_p('MAKEFILE = %s', _MAKE.getmakefilename(prj, true))
			_p('')
		end
	end



--
-- Write a block of configuration settings.
--

	local function is_excluded(prj, cfg, file)
		if table.icontains(prj.excludes, file) then
			return true
		end

		if table.icontains(cfg.excludes, file) then
			return true
		end

		return false
	end

	function premake.gmake_valac_config(prj, cfg, valac)

		_p('ifeq ($(config),%s)', _MAKE.esc(cfg.shortname))

		_p('  BASEDIR    = %s', _MAKE.esc(path.getrelative(cfg.location, _WORKING_DIR)))
		_p('  OBJDIR     = %s', _MAKE.esc(cfg.objectsdir))
		_p('  TARGETDIR  = %s', _MAKE.esc(cfg.buildtarget.directory))
		_p('  TARGET     = $(TARGETDIR)/%s', _MAKE.esc(cfg.buildtarget.name))
		_p('  DEFINES    +=%s', make.list(valac.getdefines(cfg.defines)))
		_p('  VAPIDIRS   +=%s', make.list(valac.getvapidirs(cfg.vapidirs)))
		_p('  PKGS       +=%s', make.list(valac.getlinks(cfg.links)))
		_p('  FLAGS      += $(DEFINES) $(VAPIDIRS) $(PKGS)%s', make.list(table.join(valac.getvalaflags(cfg), cfg.buildoptions_vala)))
		_p('  ALL_LDFLAGS+= $(LDFLAGS)%s', make.list(table.join(cfg.linkoptions)))
		_p('  LINKOBJS    = %s', "$(OBJECTS)")
		_p('  ALL_CFLAGS += $(CFLAGS) $(ARCH)%s', make.list(table.join(cfg.buildoptions, cfg.buildoptions_c, valac.getvalaccflags(cfg))))
		_p('  LINKCMD     = $(CC) -o $(TARGET) $(LINKOBJS) $(ARCH) $(ALL_LDFLAGS)');

		table.sort(cfg.files)

		-- add objects for compilation, and remove any that are excluded per config.
		_p('  OBJECTS := \\')
		for _, file in ipairs(cfg.files) do
			if path.issourcefile(file) or path.isgresource(file) then
				if not is_excluded(prj, cfg, file) then
					_p('\t$(OBJDIR)/%s.o \\'
						, _MAKE.esc(path.trimdots(path.removeext(file)))
						)
				end
			end
		end
		_p('')

		_p('  SOURCES := \\')
		for _, file in ipairs(cfg.files) do
			if path.issourcefile(file) and path.isvalafile(file) then
				if not is_excluded(prj, cfg, file) then
					_p('\t%s \\', _MAKE.esc(file))
				end
			end
		end
		_p('')

		_p('  GRESOURCES := \\')
		for _, file in ipairs(cfg.files) do
			if path.isgresource(file) then
				if not is_excluded(prj, cfg, file) then
					_p('\t%s \\', _MAKE.esc(file))
				end
			end
		end
		_p('')

		_p('  define PREBUILDCMDS')
		if #cfg.prebuildcommands > 0 then
			_p('\t@echo Running pre-build commands')
			_p('\t%s', table.implode(cfg.prebuildcommands, "", "", "\n\t"))
		end
		_p('  endef')

		_p('  define PRELINKCMDS')
		if #cfg.prelinkcommands > 0 then
			_p('\t@echo Running pre-link commands')
			_p('\t%s', table.implode(cfg.prelinkcommands, "", "", "\n\t"))
		end
		_p('  endef')

		_p('  define POSTBUILDCMDS')
		if #cfg.postbuildcommands > 0 then
			_p('\t@echo Running post-build commands')
			_p('\t%s', table.implode(cfg.postbuildcommands, "", "", "\n\t"))
		end
		_p('  endef')

		_p('endif')
		_p('')
	end



--
-- Build command for a single file.
--

	function vala.fileRules(prj, cc)
		local platforms = premake.filterplatforms(prj.solution, cc.platforms, "Native")

		_p('ifneq (,$(OBJRESP))')
		_p('$(OBJRESP): $(OBJECTS) | $(TARGETDIR) $(OBJDIRS)')
		_p('\t$(SILENT) echo $^')
		_p('\t$(SILENT) echo $^ > $@')
		_p('endif')
		_p('')

		_p('ifneq (,$(LDRESP))')
		_p('$(LDRESP): $(LDDEPS) | $(TARGETDIR) $(OBJDIRS)')
		_p('\t$(SILENT) echo $^')
		_p('\t$(SILENT) echo $^ > $@')
		_p('endif')
		_p('')

		local pattern_targets = ""
		table.sort(prj.allfiles)
		for _, file in ipairs(prj.allfiles or {}) do
			if path.issourcefile(file) or path.isgresource(file) then
				if path.isvalafile(file) or path.iscfile(file) or path.isgresource(file) then
					if path.isvalafile(file) or path.isgresource(file) then
						_p('$(OBJDIR)/%s.o: $(OBJDIR)/%s.c $(GCH) $(MAKEFILE) | $(OBJDIR)/%s'
							, _MAKE.esc(path.trimdots(path.removeext(file)))
							, _MAKE.esc(path.trimdots(path.removeext(file)))
							, _MAKE.esc(path.getdirectory(path.trimdots(file)))
							)
						if not path.isgresource(file) then
							pattern_targets = pattern_targets
								.. "$(OBJDIR)/"
								.. _MAKE.esc(path.trimdots(path.removeext(file)))
								.. ".% \\\n" -- Pattern rule: https://stackoverflow.com/a/3077254
						end
					else
						_p('$(OBJDIR)/%s.o: %s $(GCH) $(MAKEFILE) | $(OBJDIR)/%s'
							, _MAKE.esc(path.trimdots(path.removeext(file)))
							, file
							, _MAKE.esc(path.getdirectory(path.trimdots(file)))
							)
					end
					if prj.msgcompile then
						_p('\t@echo ' .. prj.msgcompile)
					else
						_p('\t@echo $(notdir $<)')
					end
					_p('\t$(SILENT) %s $(FORCE_INCLUDE) -o "$@" -c "$<"'
						, "$(CC) $(ALL_CFLAGS)"
						, _MAKE.esc(path.getbasename(file))
						)
					for _, task in ipairs(prj.postcompiletasks or {}) do
						_p('\t$(SILENT) %s', task)
						_p('')
					end
					_p('')

					if path.isgresource(file) then
						_p('$(OBJDIR)/%s.c: %s $(GCH) $(MAKEFILE) | $(OBJDIR)/%s'
							, _MAKE.esc(path.trimdots(path.removeext(file)))
							, _MAKE.esc(file)
							, _MAKE.esc(path.getdirectory(path.trimdots(file)))
							)
						if prj.msgcompile then
							_p('\t@echo ' .. prj.msgcompile)
						else
							_p('\t@echo $(notdir $<)')
						end
						_p('\t$(SILENT) %s "$<" --target="$@" --sourcedir="%s" --generate'
							, "$(GLIBRC)"
							, _MAKE.esc(path.getdirectory(file))
							)
						for _, task in ipairs(prj.postcompiletasks or {}) do
							_p('\t$(SILENT) %s', task)
							_p('')
						end
					end

					_p('')
				end
			end
		end

		if pattern_targets ~= "" then
			-- Generate .c from corresponding .vala
			_p('%s: $(SOURCES) $(GRESOURCES) $(GCH) $(MAKEFILE)', pattern_targets)
			if prj.msgcompile then
				_p('\t@echo ' .. prj.msgcompile)
			else
				_p('\t@echo [GEN] valac')
			end
			_p('\t$(SILENT) %s $(SOURCES) --directory $(OBJDIR) --basedir $(BASEDIR) --gresources=$(GRESOURCES) -C > /dev/null'
				, "$(VALAC) $(FLAGS)"
				)
			for _, task in ipairs(prj.postcompiletasks or {}) do
				_p('\t$(SILENT) %s', task)
				_p('')
			end
		end
	end
